package it.softphone.cmc.server.rest;

import static it.softphone.commons.util.LogUtil.logger;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.Providers;

import org.jvnet.mimepull.Header;
import org.jvnet.mimepull.MIMEConfig;
import org.jvnet.mimepull.MIMEMessage;
import org.jvnet.mimepull.MIMEParsingException;
import org.jvnet.mimepull.MIMEPart;

import com.sun.jersey.core.header.MediaTypes;
import com.sun.jersey.multipart.BodyPart;
import com.sun.jersey.multipart.BodyPartEntity;
import com.sun.jersey.multipart.FormDataBodyPart;
import com.sun.jersey.multipart.FormDataMultiPart;
import com.sun.jersey.multipart.MultiPart;
import com.sun.jersey.multipart.MultiPartConfig;
import com.sun.jersey.spi.CloseableService;

/**
 * Replaces build-in Jersey multipart reader to fix GAE contraints about temporary file ceation.
 *
 * @see com.sun.jersey.multipart.impl.MultiPartReader
 */
@Provider
@Consumes("multipart/*")
public class CMCMultiPartReader implements MessageBodyReader<MultiPart> {
    private final Providers providers;
    
	//private final MultiPartConfig config;
 
    private final CloseableService closeableService;
 
    private final MIMEConfig mimeConfig;
 
    // -------------------------------------------------------
    // -                        LOGIC                        -
    // -------------------------------------------------------
 
    private String getFixedHeaderValue(Header h) {
        String result = h.getValue();
 
        if (h.getName().equals("Content-Disposition") && (result.indexOf("filename=") != -1)) {
            try {
                result = new String(result.getBytes(), "utf8");
            } catch (UnsupportedEncodingException e) {            
                final String msg = "Can't convert header \"Content-Disposition\" to UTF8 format.";
                logger.error(msg,e);
				throw new RuntimeException(msg);
            }
        }
 
        return result;
    }
 
    public CMCMultiPartReader(@Context Providers providers, @Context MultiPartConfig config,
        @Context CloseableService closeableService) {
        this.providers = providers;
 
        if (config == null) {
            final String msg = "The MultiPartConfig instance we expected is not present. "
                + "Have you registered the MultiPartConfigProvider class?";
            logger.error( msg );
			throw new IllegalArgumentException(msg);
        }
 
        //this.config = config;
       
        this.closeableService = closeableService;
 
        mimeConfig = new MIMEConfig();
        //mimeConfig.setMemoryThreshold(config.getBufferThreshold());
        mimeConfig.setMemoryThreshold(-1L);
    }
 
    @Override
    public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return MultiPart.class.isAssignableFrom(type);
    }
 
    @Override
    public MultiPart readFrom(Class<MultiPart> type, Type genericType, Annotation[] annotations, MediaType mediaType,
        MultivaluedMap<String, String> headers, InputStream stream) throws IOException, WebApplicationException {
        try {
            MIMEMessage mm = new MIMEMessage(stream, mediaType.getParameters().get("boundary"), mimeConfig);
 
            boolean formData = false;
            MultiPart multiPart = null;
 
            if (MediaTypes.typeEquals(mediaType, MediaType.MULTIPART_FORM_DATA_TYPE)) {
                multiPart = new FormDataMultiPart();
                formData = true;
            } else {
                multiPart = new MultiPart();
            }
 
            multiPart.setProviders(providers);
 
            if (!formData) {
                multiPart.setMediaType(mediaType);
            }
 
            for (MIMEPart mp : mm.getAttachments()) {
                BodyPart bodyPart = null;
 
                if (formData) {
                    bodyPart = new FormDataBodyPart();
                } else {
                    bodyPart = new BodyPart();
                }
 
                bodyPart.setProviders(providers);
 
                for (Header h : mp.getAllHeaders()) {
                    bodyPart.getHeaders().add(h.getName(), getFixedHeaderValue(h));
                }
 
                try {
                    String contentType = bodyPart.getHeaders().getFirst("Content-Type");
 
                    if (contentType != null) {
                        bodyPart.setMediaType(MediaType.valueOf(contentType));
                    }
 
                    bodyPart.getContentDisposition();
                } catch (IllegalArgumentException ex) {
                	logger.error( "readFrom error", ex );
                    throw new WebApplicationException(ex, 400);
                }
 
                bodyPart.setEntity(new BodyPartEntity(mp));
                multiPart.getBodyParts().add(bodyPart);
            }
 
            if (closeableService != null) {
                closeableService.add(multiPart);
            }
 
            return multiPart;
        } catch (MIMEParsingException ex) {
        	logger.error( "readFrom error", ex );
            throw new WebApplicationException(ex, 400);
        }
    }
 
}
